#include "queue.h"
#include "sem.h"

// 客户端和服务端基于环形队列进行通讯
// 环形队列(基于共享内存)相关函数

// 环形队列初始化
shm_fifo_t *shm_fifo_init(int blocks, int blksz)
{
    int shmsz = 0;
    int shmid;
    unsigned short values[] = {0, blocks, 1}; // 3个信号量初始化
    shm_fifo_t *p_shm_fifo = (shm_fifo_t *)malloc(sizeof(shm_fifo_t));
    enum shm_create_status shm_status;

    // 环形队列大小就是共享内存大小
    shmsz = sizeof(shm_head_t) + blksz * blocks; // 共享内存大小
    shm_status = shm_create(shmsz, &shmid);
    if (shm_status != SHM_CREATE_ERROR)
    {
        p_shm_fifo->phead = (shm_head_t *)shm_at(shmid); // 地址映射
        if (shm_status == SHM_CREATE_NEW)
        {
            p_shm_fifo->phead->rpos = 0; // 读指针 指向下一个要读取的数据块
            p_shm_fifo->phead->wpos = 0; // 写指针 指向下一个要写入的数据块
            p_shm_fifo->phead->bloksz = blksz;
            p_shm_fifo->phead->blocks = blocks;
            p_shm_fifo->phead->semid = sem_create(3, values);
        }

        p_shm_fifo->p_playload = (char *)(p_shm_fifo->phead + 1);

        return p_shm_fifo;
    }

    return NULL;
}

// 环形队列的销毁
void shm_fifo_destroy(shm_fifo_t *p_shm_fifo, int shmid)
{
    sem_delete(p_shm_fifo->phead->semid); // 删除信号量集合
    shm_dt(p_shm_fifo->phead);            // 解除映射
    shm_delete(shmid);                    // 删除共享内存
    free(p_shm_fifo);
}

// 队列写数据
void shm_fifo_write(shm_fifo_t *pfifo, void *buf)
{
    int pos = 0;

    sem_p(pfifo->phead->semid, SEM_FULL_ID);
    sem_p(pfifo->phead->semid, SEM_MUTEX_ID);                             // 互斥量
    pos = pfifo->phead->wpos * pfifo->phead->bloksz;                      // 计算写入位置
    memcpy(pfifo->p_playload + pos, buf, pfifo->phead->bloksz);           // 从buf写入数据到 队列
    pfifo->phead->wpos = (pfifo->phead->wpos + 1) % pfifo->phead->blocks; // 写指针加1
    sem_v(pfifo->phead->semid, SEM_MUTEX_ID);
    sem_v(pfifo->phead->semid, SEM_EMPTY_ID);
}

// 队列读数据
void shm_fifo_read(shm_fifo_t *pfifo, void *buf)
{
    int pos = 0;

    sem_p(pfifo->phead->semid, SEM_EMPTY_ID);
    sem_p(pfifo->phead->semid, SEM_MUTEX_ID);                             // 互斥量
    pos = pfifo->phead->rpos * pfifo->phead->bloksz;                      // 计算读位置
    memcpy(buf, pfifo->p_playload + pos, pfifo->phead->bloksz);           // 写入数据
    pfifo->phead->rpos = (pfifo->phead->rpos + 1) % pfifo->phead->blocks; // 写指针加1
    sem_v(pfifo->phead->semid, SEM_MUTEX_ID);
    sem_v(pfifo->phead->semid, SEM_FULL_ID);
}
